package com.tsfyun.scm.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tsfyun.common.base.config.OrikaBeanMapper;
import com.tsfyun.common.base.enums.RateTimeEnum;
import com.tsfyun.common.base.enums.RateTypeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.extension.OrderItem;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.common.base.util.*;
import com.tsfyun.common.base.vo.CurrencyVO;
import com.tsfyun.scm.system.dto.BankChinaRateDTO;
import com.tsfyun.scm.system.entity.BankChinaRate;
import com.tsfyun.scm.system.entity.Currency;
import com.tsfyun.scm.system.mapper.BankChinaRateMapper;
import com.tsfyun.scm.system.service.IBankChinaRateService;
import com.tsfyun.scm.system.service.ICurrencyService;
import com.tsfyun.scm.system.util.RateUtil;
import com.tsfyun.scm.system.util.TsfWeekendSqls;
import com.tsfyun.scm.system.vo.BankChinaRateVO;
import com.tsfyun.scm.system.vo.IndexHalfMonthRateVO;
import com.tsfyun.scm.system.vo.PeriodBankChinaRateVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 中行汇率服务实现类
 * </p>
 *

 * @since 2020-03-17
 */
@Slf4j
@Service
public class BankChinaRateServiceImpl extends ServiceImpl<BankChinaRate> implements IBankChinaRateService {

    @Autowired
    private OrikaBeanMapper beanMapper;

    @Autowired
    private BankChinaRateMapper bankChinaRateMapper;

    @Autowired
    private ICurrencyService currencyService;

    @Override
    public PageInfo<BankChinaRate> page(BankChinaRateDTO dto) {
        TsfWeekendSqls sqls = TsfWeekendSqls.<BankChinaRate>custom()
                .andEqualTo(true, BankChinaRate::getCurrencyId, dto.getCurrencyId())
                .andGreaterThanOrEqualTo(true, BankChinaRate::getPublishDate, dto.getPublishDate())
                .andLessThanOrEqualTo(true, BankChinaRate::getPublishDate, LocalDateTimeUtils.formatTime(LocalDateTimeUtils.convertDateToLDT(dto.getPublishDate()), "yyyy-MM-dd 23:59:59"));
        PageInfo<BankChinaRate> pageInfo = super.pageList(dto.getPage(), dto.getLimit(), sqls, Lists.newArrayList(OrderItem.desc("publishDate"), OrderItem.desc("currencyId")));
        return pageInfo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(BankChinaRateDTO dto) {
        Currency currency = currencyService.getById(dto.getCurrencyId());
        TsfPreconditions.checkArgument(Objects.nonNull(currency), new ServiceException("币制信息不存在"));
        BankChinaRate bankChinaRate = new BankChinaRate();
        bankChinaRate.setCurrencyId(dto.getCurrencyId());
        bankChinaRate.setBocConversion(dto.getBocConversion());
        bankChinaRate.setBuyingRate(dto.getBuyingRate());
        bankChinaRate.setCashPurchase(dto.getCashPurchase());
        bankChinaRate.setCashSelling(dto.getCashSelling());
        bankChinaRate.setCurrencyId(dto.getCurrencyId());
        bankChinaRate.setCurrencyName(dto.getCurrencyName());
        bankChinaRate.setSpotSelling(dto.getSpotSelling());
        bankChinaRate.setCurrencyName(currency.getName());
        bankChinaRate.setPublishDate(LocalDateTimeUtils.convertDateToLDT(dto.getPublishDate()));
        bankChinaRate.setId(dto.getCurrencyId().concat(LocalDateTimeUtils.formatTime(LocalDateTimeUtils.convertDateToLDT(dto.getPublishDate()), "yyyyMMddHHmmss")));
        super.saveNonNull(bankChinaRate);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(BankChinaRateDTO dto) {
        BankChinaRate bankChinaRate = super.getById(dto.getPreId());
        TsfPreconditions.checkArgument(Objects.nonNull(bankChinaRate), new ServiceException("数据不存在"));
        Currency currency = currencyService.getById(dto.getCurrencyId());
        TsfPreconditions.checkArgument(Objects.nonNull(currency), new ServiceException("币制信息不存在"));
        dto.setCurrencyName(currency.getName());
        //此处需要注意id的变化
        dto.setId(dto.getCurrencyId().concat(LocalDateTimeUtils.formatTime(LocalDateTimeUtils.convertDateToLDT(dto.getPublishDate()), "yyyyMMddHHmmss")));
        bankChinaRateMapper.updateRate(dto);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(String id) {
        BankChinaRate bankChinaRate = super.getById(id);
        TsfPreconditions.checkArgument(Objects.nonNull(bankChinaRate), new ServiceException("数据不存在"));
        super.removeById(id);
    }

    @Override
    public BankChinaRateVO detail(String id) {
        BankChinaRate bankChinaRate = super.getById(id);
        TsfPreconditions.checkArgument(Objects.nonNull(bankChinaRate), new ServiceException("数据不存在"));
        return beanMapper.map(bankChinaRate, BankChinaRateVO.class);
    }

    @Override
    public BankChinaRate findLastOne(String currencyCode, String date) {
        return bankChinaRateMapper.findLastOne(currencyCode, date);
    }

    @Override
    public void grabRate(String date, Currency currency) {
        List<BankChinaRate> bankChinaRates = new ArrayList<>();
        //防止数据重复
        List<String> ids = new ArrayList<>();
        RateUtil.startGrab(date, currency, 1, bankChinaRates, ids);
        if (bankChinaRates.size() > 0) {
            log.info(currency.getName() + "：本次更新[" + bankChinaRates.size() + "]条数据");
            bankChinaRates.sort((a, b) ->
                    b.getPublishDate().compareTo(a.getPublishDate())
            );
            //批量保存汇率
            bankChinaRateMapper.batchInsert(bankChinaRates);
        }
    }

    @Override
    public BigDecimal obtain(String currency, String reteType, String data) {
        BankChinaRate chinaRate = bankChinaRateMapper.selectRate(currency, data);
        if (Objects.nonNull(chinaRate)) {
            RateTypeEnum rateTypeEnum = RateTypeEnum.of(reteType);
            if (Objects.equals(RateTypeEnum.PAY_BANK_CHINA_SELL, rateTypeEnum) || Objects.equals(RateTypeEnum.IMP_BANK_CHINA_SELL, rateTypeEnum)) {
                //优先取现汇卖出价 再取 现钞卖出价
                BigDecimal rate = Objects.nonNull(chinaRate.getSpotSelling()) ? chinaRate.getSpotSelling() : chinaRate.getCashSelling();
                return rate.divide(BigDecimal.valueOf(100), 6, BigDecimal.ROUND_HALF_UP);
            }
        }
        return null;
    }

    @Override
    public List<IndexHalfMonthRateVO> recentlyHalfMonthBankRate(String rateTime) {
        final String calRateTime = TypeUtils.castToString(rateTime, RateTimeEnum.TIME1.getCode());
        Optional.ofNullable(RateTimeEnum.of(calRateTime)).orElseThrow(() -> new ServiceException("汇率时间错误"));
        List<IndexHalfMonthRateVO> dataList = Lists.newArrayList();
        List<CurrencyVO> currencys = currencyService.select();
        if (CollUtil.isEmpty(currencys)) {
            log.error("没有可用币制");
            return dataList;
        }
        LocalDateTime now = LocalDateTime.now();
        String startTime = LocalDateTimeUtils.formatTime(now.plusDays(-15), "yyyy-MM-dd 00:00:00");
        List<PeriodBankChinaRateVO> bankChinaRates = bankChinaRateMapper.getPeriodRates(calRateTime.replace(":", ""), startTime);
        Map<String, List<PeriodBankChinaRateVO>> periodBankChinaRatesMap = bankChinaRates.stream().collect(Collectors.groupingBy(PeriodBankChinaRateVO::getCurrencyId));
        currencys.stream().forEach(currencyVO -> {
            List<PeriodBankChinaRateVO> periodBankChinaRateVOS = periodBankChinaRatesMap.get(currencyVO.getId());
            if (CollUtil.isNotEmpty(periodBankChinaRateVOS)) {
                //按日期升序排列
                periodBankChinaRateVOS = periodBankChinaRateVOS.stream().sorted(Comparator.comparing(PeriodBankChinaRateVO::getDate)).collect(Collectors.toList());
                periodBankChinaRateVOS.stream().forEach(bankChinaRate -> {
                    IndexHalfMonthRateVO indexHalfMonthRateVO = new IndexHalfMonthRateVO();
                    indexHalfMonthRateVO.setCurrencyName(bankChinaRate.getCurrencyName());
                    indexHalfMonthRateVO.setDate(DateUtils.format(DateUtils.parse(bankChinaRate.getDate(), "yyyyMMdd"), "yyyy-MM-dd"));
                    //优先取现汇卖出价 再取 现钞卖出价
                    BigDecimal rate = Objects.nonNull(bankChinaRate.getSpotSelling()) ? bankChinaRate.getSpotSelling() : bankChinaRate.getCashSelling();
                    rate = rate.divide(BigDecimal.valueOf(100), 6, BigDecimal.ROUND_HALF_UP);
                    indexHalfMonthRateVO.setRate(rate.toString());
                    dataList.add(indexHalfMonthRateVO);
                });
            }
        });
        return dataList;
    }

    @Override
    public List<IndexHalfMonthRateVO> currentBankRate(String rateTime, String rateDate) {
        final String calRateTime = TypeUtils.castToString(rateTime, RateTimeEnum.TIME1.getCode());
        Optional.ofNullable(RateTimeEnum.of(calRateTime)).orElseThrow(() -> new ServiceException("汇率时间错误"));
        List<IndexHalfMonthRateVO> dataList = Lists.newArrayList();
        //获取所有启用的币制
        List<CurrencyVO> currencyVOList = currencyService.select();
        rateDate = StringUtils.isEmpty(rateTime) ? LocalDateTimeUtils.formatTime(LocalDateTime.now(), "yyyy-MM-dd") : rateDate;
        List<PeriodBankChinaRateVO> bankChinaRates = bankChinaRateMapper.getCurrentDateRates(calRateTime.replace(":", ""), rateDate);
        if (!CollectionUtils.isEmpty(bankChinaRates)) {
            List<IndexHalfMonthRateVO> tempList  =Lists.newArrayListWithExpectedSize(currencyVOList.size());
            bankChinaRates.stream().forEach(bankChinaRate -> {
                IndexHalfMonthRateVO indexHalfMonthRateVO = new IndexHalfMonthRateVO();
                indexHalfMonthRateVO.setCurrencyName(bankChinaRate.getCurrencyName());
                indexHalfMonthRateVO.setCurrencyCode(bankChinaRate.getCurrencyId());
                indexHalfMonthRateVO.setDate(DateUtils.format(DateUtils.parse(bankChinaRate.getDate(), "yyyyMMdd"), "yyyy-MM-dd"));
                //优先取现汇卖出价 再取 现钞卖出价
                BigDecimal rate = Objects.nonNull(bankChinaRate.getSpotSelling()) ? bankChinaRate.getSpotSelling() : bankChinaRate.getCashSelling();
                rate = rate.divide(BigDecimal.valueOf(100), 6, BigDecimal.ROUND_HALF_UP);
                indexHalfMonthRateVO.setRate(rate.toString());
                tempList.add(indexHalfMonthRateVO);
            });
            //按币制分组
            Map<String,IndexHalfMonthRateVO> rateMap = tempList.stream().collect(Collectors.toMap(IndexHalfMonthRateVO::getCurrencyCode,Function.identity()));
            String finalRateDate1 = rateDate;
            currencyVOList.stream().forEach(cur -> {
                IndexHalfMonthRateVO indexHalfMonthRateVO = rateMap.get(cur.getId());
                if(Objects.isNull(indexHalfMonthRateVO)) {
                    indexHalfMonthRateVO = new IndexHalfMonthRateVO();
                    indexHalfMonthRateVO.setDate(finalRateDate1);
                    indexHalfMonthRateVO.setCurrencyName(cur.getName());
                    indexHalfMonthRateVO.setCurrencyCode(cur.getId());
                    indexHalfMonthRateVO.setRate(null);
                }
                dataList.add(indexHalfMonthRateVO);
            });
        } else {
            String finalRateDate = rateDate;
            currencyVOList.stream().forEach(cur -> {
                if(!"CNY".equals(cur.getId())){
                    IndexHalfMonthRateVO rateVO = new IndexHalfMonthRateVO();
                    rateVO.setDate(finalRateDate);
                    rateVO.setCurrencyName(cur.getName());
                    rateVO.setCurrencyCode(cur.getId());
                    rateVO.setRate(null);
                    dataList.add(rateVO);
                }
            });
        }
        return dataList;
    }

    @Override
    public void grab() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try{
            //获取所有币制
            List<Currency> currencyList = currencyService.list();
            if(currencyList == null || currencyList.size() < 1){return;}
            String date = DateUtils.getCurrentDate();
            for(Currency currency : currencyList){
                if(!Objects.equals("CNY",currency.getId())){
                    grabRate(date,currency);
                    try{
                        Thread.sleep(Long.valueOf(500));
                    }catch (Exception e){

                    }
                }
            }
            stopWatch.stop();
            log.info("扫描中行汇率数据结束......，本次抓取耗时:{}s",stopWatch.getTotalTimeSeconds());
        } catch (Exception e) {
            log.error("定时扫描中行汇率数据失败",e);
        }
    }
}
